Imports Microsoft.SqlServer.Dts.Runtime
Imports System.IO
Imports System.Web.UI
Imports System.Web

' The LogProviderType property is required but not used.
'  The custom log provider will not appear in the list
'  if a LogProviderType is not provided.
<System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1724:TypeNamesShouldNotMatchNamespaces")> _
<System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1001:TypesThatOwnDisposableFieldsShouldBeDisposable")> _
<DtsLogProvider(DisplayName:="Custom log provider for HTML files (VB)", _
Description:="Writes log entries for events to an HTML file", _
LogProviderType:="Custom")> _
Public Class HtmlLogProviderVB
    Inherits LogProviderBase

#Region " Variables and constants "

    ' Constants.
    Private Const SUBCOMPONENT As String = "HtmlLogProviderVB"
    Private Const PACKAGE_END_EVENT As String = "PackageEnd"

    ' Variables.
    Private _connections As Microsoft.SqlServer.Dts.Runtime.Connections
    Private _events As Microsoft.SqlServer.Dts.Runtime.IDTSInfoEvents
    Private _configString As String
    Private _logFile As String
    Private _htmlLogWriter As HtmlLogWriterVB
    Private _logStream As MemoryStream
    Private _logStreamWriter As StreamWriter

    ' Status flags.
    Private _fireEventsAgain As Boolean
    Private _loggingAlreadyStarted As Boolean
    Private _packageHasEnded As Boolean

#End Region

#Region " Initialize and Validate "

    Public Overrides Sub InitializeLogProvider(ByVal connections As Microsoft.SqlServer.Dts.Runtime.Connections, ByVal events As Microsoft.SqlServer.Dts.Runtime.IDTSInfoEvents, ByVal refTracker As Microsoft.SqlServer.Dts.Runtime.ObjectReferenceTracker)

        ' Cache object references for later use.
        _connections = connections
        _events = events

        _fireEventsAgain = True

    End Sub

    <System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")> _
    Public Overrides Function Validate(ByVal events As Microsoft.SqlServer.Dts.Runtime.IDTSInfoEvents) As Microsoft.SqlServer.Dts.Runtime.DTSExecResult
        If events Is Nothing Then
            Throw New ArgumentNullException("events")
        End If

        Dim fileExtension As String

        _configString = Me.ConfigString

        ' Configuration string should contain the name of the selected File connection manager.
        ' Validate function needs to validate:
        ' 1. Has a config string been provided?
        ' 2. Does the specified connection manager exist?
        ' 3. Can the connection string be retrieved successfully?
        ' 4. Is the connection string empty?
        ' 5. Is the specified file an HTML file with an appropriate extension?
        ' Do not check for file existence, because user may have
        '  specified to create a new file at run time.

        If _configString.Length = 0 Then
            ' Validation test 1: Has a config string been provided?
            events.FireError(0, SUBCOMPONENT, _
              "The log provider did not receive connection information.", _
              String.Empty, 0)
            Return DTSExecResult.Failure
        ElseIf Not _connections.Contains(_configString) Then
            ' Validation test 2: Does the specified connection manager exist?
            events.FireError(0, SUBCOMPONENT, _
              "The specified connection manager cannot be found.", _
              String.Empty, 0)
            Return DTSExecResult.Failure
        Else
            ' Validation test 3: Can the connection string be retrieved successfully?
            Try
                _logFile = DirectCast(_connections(_configString).AcquireConnection(Nothing), String)
            Catch
                events.FireError(0, SUBCOMPONENT, _
                  "The specified connection manager did not return a filename string.", _
                  String.Empty, 0)
                Return DTSExecResult.Failure
            End Try
        End If

        If String.IsNullOrEmpty(_logFile) Then
            ' Validation test 4: Is the connection string empty?
            events.FireError(0, SUBCOMPONENT, _
              "The specified connection manager does not contain a filename.", _
              String.Empty, 0)
            Return DTSExecResult.Failure
        Else
            ' Validation test 5: Is the specified file an HTML file with an appropriate extension?
            fileExtension = _logFile.Substring(_logFile.LastIndexOf(".") + 1)
            If Not (fileExtension = "htm" OrElse fileExtension = "html") Then
                events.FireError(0, SUBCOMPONENT, _
                  "The filename specified by the connection manager does not have an .htm or .html extension.", _
                  String.Empty, 0)
                Return DTSExecResult.Failure
            End If
        End If

        Return DTSExecResult.Success

    End Function

#End Region

#Region " OpenLog and CloseLog "

    Public Overrides Sub OpenLog()

        Dim subComponentInfo As String = _
          SUBCOMPONENT & "-OpenLog "

        If _fireEventsAgain Then
            _events.FireInformation(0, subComponentInfo, "Opening HTML log.", String.Empty, 0, _fireEventsAgain)
        End If

        If Not _loggingAlreadyStarted Then

            _logStream = New MemoryStream()
            _logStreamWriter = New StreamWriter(_logStream)

            _htmlLogWriter = New HtmlLogWriterVB()
            _htmlLogWriter.InitializeLogWriter(_connections, _events, SUBCOMPONENT, _fireEventsAgain)
            _htmlLogWriter.OpenLogHtml(_logStreamWriter)

            _loggingAlreadyStarted = True

        End If

    End Sub

    <System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")> _
    Public Overrides Sub CloseLog()

        Dim logStreamReader As StreamReader
        Dim logOutput As String
        Dim fileStreamWriter As StreamWriter

        Dim subComponentInfo As String = _
          SUBCOMPONENT & "-CloseLog"

        If _fireEventsAgain Then
            _events.FireInformation(0, subComponentInfo, "Closing HTML log.", String.Empty, 0, _fireEventsAgain)
        End If

        ' The following IF test is necessary because
        '  OpenLog and CloseLog are called multiple times.

        If _packageHasEnded Then

            Try

                ' Get cached log output and clean up.
                _htmlLogWriter.CloseLogHtml()

                _logStreamWriter.Flush()
                _logStream.Position = 0
                logStreamReader = New StreamReader(_logStream)
                logOutput = logStreamReader.ReadToEnd()

                _logStreamWriter.Close()
                logStreamReader.Close()

                ' Write cached log output to log file all at once.
                fileStreamWriter = New StreamWriter(_logFile)
                With fileStreamWriter
                    .Write(logOutput)
                    .Close()
                End With

            Catch ex As Exception
                _events.FireError(0, subComponentInfo, ex.Message, String.Empty, 0)
            End Try

        End If

    End Sub

#End Region

#Region " Log: Write individual log entries "

    Public Overrides Sub Log(ByVal logEntryName As String, ByVal computerName As String, _
      ByVal operatorName As String, ByVal sourceName As String, _
      ByVal sourceID As String, ByVal executionID As String, _
      ByVal messageText As String, ByVal startTime As Date, ByVal endTime As Date, _
      ByVal dataCode As Integer, ByVal dataBytes() As Byte)

        Dim subComponentInfo As String = _
         SUBCOMPONENT & "-Log"

        If _fireEventsAgain Then
            _events.FireInformation(0, subComponentInfo, "Writing HTML log entry for " & logEntryName, _
            String.Empty, 0, _fireEventsAgain)
        End If

        _htmlLogWriter.LogHtml(logEntryName, computerName, operatorName, _
            sourceName, sourceID, executionID, _
            messageText, startTime, endTime, _
            dataCode, dataBytes)

        If logEntryName = PACKAGE_END_EVENT Then
            _packageHasEnded = True
        End If

    End Sub

#End Region

End Class
